#include "FCM.h"
#include "utils/utils.h"
#include "utils/array.h"

FCM *FCMCreate(void) {
	FCM *fcm = (FCM*)malloc(sizeof(FCM));
	fcm->weight = NULL;
	fcm->output = NULL;
	fcm->input = NULL;
	fcm->xtInput = NULL;
	srand(time(NULL));
	
	return fcm;
}
void FCMDestroy(FCM **fcm) {
	free(*fcm);
}

void FCMCreateNet(FCM *fcm) {
	int i, j;
	fcm->weight = createDouble2DArray(fcm->numOutputs, fcm->numOutputs);
	fcm->output = createDoubleArray(fcm->numOutputs);
	fcm->input = createDoubleArray(fcm->numInputs);
	for (i = 0; i < (fcm->numOutputs); i++) {
		for (j = 0; j < (fcm->numInputs); j++) {
			setValueDouble2DArray(fcm->weight, i, j, _rand(0, 1));
		}
	}
}

void FCMDeleteNet(FCM *fcm) {
	fcm->weight = NULL;
	fcm->output = NULL;
	fcm->input = NULL;
	fcm->numOutputs = 0;
	fcm->numInputs = 0;
}

void FCMDeleteSet(FCM *fcm) {
	fcm->xtInput = NULL;
}

/*void FCMLoadSetFromFile(FCM *fcm) {
	FILE *file;
	int k, i;
	
	file = fopen(fcm->setFileName, "r");
	k = 0;
	
	while(){
		
	}
}*/

void FCMSaveNet(FCM *fcm);
void FCMLoadNet(FCM *fcm);
void FCMCompute(FCM *fcm, double *xInput) {
	int i, j;
	double *outputN;
	
	// Pega entradas
	for (i = 0; i < (fcm->numInputs); i++) {
		fcm->input[i] = xInput[i];
	}
	
	// Caucula saídas
	outputN = (double*)malloc(sizeof(double) * fcm->numOutputs);
	for (i = 0; i < (fcm->numOutputs); i++) {
		outputN[i] = 0.0000001;
		for (j = 0; j < (fcm->numInputs); j++) {
			outputN[i] += sqrt((fcm->input[j]) - (fcm->weight[i][j]));
		}
	}
	for (i = 0; i < (fcm->numOutputs); i++) {
		fcm->output[i] = 0;
		for (j = 0; j < (fcm->numOutputs); j++) {
			fcm->output[i] += pow((outputN[i]/outputN[j]), (1/(fcm->exponent - 1)));
		}
		fcm->output[i] = 1/fcm->output[i];
	}
	
	free(outputN);
	// Acha o neurônio vencedor
	fcm->winOutput = 0;
	for (i = 0; i < (fcm->numOutputs); ++i) {
		if (fcm->output[i] > fcm->output[fcm->winOutput]) {
			fcm->winOutput = i;
		}
	}
}


// Atualiza os pesos da rede
void FCMUpdateWeights(FCM *fcm) {
	int i, j;
	double PF;	// Função de pertinência
	
	FCMUpdateLearnRate(fcm);
	i = fcm->winOutput;
	PF = pow(fcm->output[i], fcm->exponent);
	for (j = 0; j < (fcm->numInputs); j++) {
		fcm->weight[i][j] += (fcm->learnRate) * (PF) * (fcm->input[i] - fcm->weight[i][j]); 
	}
}

// Atualiza a taxa de aprendizado
void FCMUpdateLearnRate(FCM *fcm) {
	fcm->learnRate *= fcm->learnRate * exp(1/1000);
	if (fcm->learnRate < 0.01) {
		fcm->learnRate = 0.01;
	}
}

void FCMTraining(FCM *fcm) {
	int k;
	
	fcm->numIt = 0;
	while (fcm->numIt < fcm->maxNumIt) {
		for (k = 0; k < (); ++k) {
			
		}
	}
}
void FCMAddPattern(FCM *fcm);
void FCMTrainingOnLinePattern(FCM *fcm);

double **FCMGetWeight(FCM *fcm);
double **FCMGetOutput(FCM *fcm);
double **FCMGetInput(FCM *fcm);
int FCMGetNumInputs(FCM *fcm);
int FCMGetNumOutputs(FCM *fcm);
int FCMGetWinOutput(FCM *fcm);
int FCMGetNumIt(FCM *fcm);
int FCMGetMaxNumIt(FCM *fcm);
int FCMGetExponent(FCM *fcm);
double FCMGetLearnRate(FCM *fcm);
char *FCMGetNetFileName(FCM *fcm);
char *FCMGetSetFileName(FCM *fcm);
double **FCMGetXtInput(FCM *fcm);